package com.wlyuan.gateway.security;

import com.wlyuan.gateway.config.SecurityProperties;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.ReactiveAuthenticationManager;
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
import org.springframework.security.config.web.server.SecurityWebFiltersOrder;
import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;
import org.springframework.security.oauth2.server.resource.web.server.ServerBearerTokenAuthenticationConverter;
import org.springframework.security.web.server.SecurityWebFilterChain;
import org.springframework.security.web.server.authentication.AuthenticationWebFilter;

import java.util.Set;
import java.util.concurrent.ConcurrentSkipListSet;

/**
 * @author yuanzheng
 */
@Slf4j
@Configuration
@EnableWebFluxSecurity
@RequiredArgsConstructor
@EnableConfigurationProperties({ResourceServerProperties.class})
public class ResourceServerConfiguration {
    @Autowired
    private SecurityProperties tokenProperties;

    @Bean
    public TokenStore tokenStore(JwtAccessTokenConverter accessTokenConverter) {
        return new JwtTokenStore(accessTokenConverter);
    }

    /**
     * 使用非对称加密算法来对Token进行签名
     */
    @Bean
    public JwtAccessTokenConverter accessTokenConverter(ResourceServerProperties resourceServerProperties) {
        logger.info("Jwt token signingKey: {}", resourceServerProperties);
        JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
        converter.setSigningKey(resourceServerProperties.getSigningKey());
        return converter;
    }

    private String[] getExcludePatterns() {
        final Set<String> excludePatterns = new ConcurrentSkipListSet<>();
        excludePatterns.add("/");
        excludePatterns.add("/error");
        excludePatterns.add("/favicon.ico");
        excludePatterns.addAll(tokenProperties.getExcludePatterns());
        return excludePatterns.toArray(new String[0]);
    }

    @Bean
    public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http, TokenStore tokenStore) {
        //token管理器
        ReactiveAuthenticationManager tokenAuthenticationManager = new ResourceAuthenticationManager(tokenStore);
        //认证过滤器
        AuthenticationWebFilter authenticationWebFilter = new AuthenticationWebFilter(tokenAuthenticationManager);
        authenticationWebFilter.setServerAuthenticationConverter(new ServerBearerTokenAuthenticationConverter());

        http.httpBasic().disable()
                .csrf().disable()
                .authorizeExchange()
                // 白名单
                .pathMatchers(getExcludePatterns()).permitAll()
                .pathMatchers(HttpMethod.OPTIONS).permitAll()
                .anyExchange().access(new ResourceAuthorizationManager())
                .and().exceptionHandling()
                .authenticationEntryPoint(new ResourceAuthenticationEntryPoint())
                .accessDeniedHandler(new ResourceAccessDeniedHandler())
                .and()
                .addFilterAt(authenticationWebFilter, SecurityWebFiltersOrder.AUTHENTICATION);
        return http.build();
    }

}
